package master.ghrm.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
public class GradableQuestion extends Question {

	private static final long serialVersionUID = 1L;

	private static final int DEF_MIN_VALUE = 1;
	private static final int DEF_MAX_VALUE = 5;

	private static final String[] EXCL_ADDITIONAL = {};

	/**
	 * Question grade label.
	 * 
	 */
	@Entity
	@Table(name = "tbl_grade_label")
	public static class GradeLabel extends AbstractBaseEntity {

		private static final long serialVersionUID = 1L;

		/**
		 * Primary Key class for grade label.
		 */
		@Embeddable
		public static class GradeLabelPK implements Serializable {

			private static final long serialVersionUID = 2057567449314128519L;

			@ManyToOne
			@JoinColumn(name = "question_id", nullable = false)
			private GradableQuestion question;

			private Integer value;

			/**
			 * Full constructor, for manual filling in.
			 * 
			 * @param aQuestion Gradable question.
			 * @param aValue value of the grade.
			 */
			public GradeLabelPK(GradableQuestion aQuestion, Integer aValue) {
				this.question = aQuestion;
				this.value = aValue;
			}

			/**
			 * Default constructor for persistence.
			 */
			public GradeLabelPK() {
				question = null;
				value = -1;
			}

			/**
			 * @return the question
			 */
			public GradableQuestion getQuestion() {
				return question;
			}

			/**
			 * @param aQuestion the question to set
			 */
			public void setQuestion(GradableQuestion aQuestion) {
				this.question = aQuestion;
			}

			/**
			 * @return the value
			 */
			public Integer getValue() {
				return value;
			}

			/**
			 * @param aValue the value to set
			 */
			public void setValue(Integer aValue) {
				this.value = aValue;
			}

			/**
			 * Eclipse generated.
			 * 
			 * @see java.lang.Object#hashCode()
			 */
			@Override
			public int hashCode() {
				final int prime = 31;
				int result = 1;
				result = prime * result + ((question == null) ? 0 : question.hashCode());
				result = prime * result + ((value == null) ? 0 : value.hashCode());
				return result;
			}

			/**
			 * Eclipse generated, modified by user: only ID of questions is checked.
			 * 
			 * @see java.lang.Object#equals(java.lang.Object)
			 */
			@Override
			public boolean equals(Object obj) {
				if (this == obj) {
					return true;
				}
				if (obj == null) {
					return false;
				}
				if (getClass() != obj.getClass()) {
					return false;
				}
				GradeLabelPK other = (GradeLabelPK) obj;
				if (question == null) {
					if (other.question != null) {
						return false;
					}
				} else if (!question.getQuestionId().equals(other.question.getQuestionId())) {
					return false;
				}
				if (value == null) {
					if (other.value != null) {
						return false;
					}
				} else if (!value.equals(other.value)) {
					return false;
				}
				return true;
			}
		}

		@Id
		private GradeLabelPK id;
		private String label;

		/**
		 * Default constructor, for persistence.
		 */
		public GradeLabel() {
			label = "";
		}

		/**
		 * Full constructor.
		 * 
		 * @param aLabel label for the grade.
		 * @param question question this label belongs to.
		 * @param value value this label is tied to to.
		 */
		public GradeLabel(String aLabel, GradableQuestion question, Integer value) {
			super();
			this.label = aLabel;
			id = new GradeLabelPK(question, value);
		}

		/**
		 * @return the label
		 */
		public String getLabel() {
			return label;
		}

		/**
		 * @param aLabel the label to set
		 */
		public void setLabel(String aLabel) {
			this.label = aLabel;
		}

		/**
		 * @return the id
		 */
		public GradeLabelPK getId() {
			return id;
		}

		/**
		 * @param anId the id to set
		 */
		public void setId(GradeLabelPK anId) {
			this.id = anId;
		}

		@Override
		protected String[] getExcludedAttributes() {
			return new String[] {};
		}
	}

	@Column(name = "min_value")
	private Integer minValue;

	@Column(name = "max_value")
	private Integer maxValue;

	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	private List<GradeLabel> labels;

	/**
	 * Default constructor, set default values for min and max values.
	 */
	public GradableQuestion() {
		this("", "", DEF_MIN_VALUE, DEF_MAX_VALUE);
	}

	/**
	 * Basic constructor, set name and text.
	 * 
	 * @param name question name.
	 * @param text text of the question.
	 */
	public GradableQuestion(String name, String text) {
		this(name, text, DEF_MIN_VALUE, DEF_MAX_VALUE);
	}

	/**
	 * Full constructor.
	 * 
	 * @param name question name
	 * @param text text of the question
	 * @param aMinValue minimal value.
	 * @param aMaxValue maximum value.
	 */
	public GradableQuestion(String name, String text, Integer aMinValue, Integer aMaxValue) {
		super(name, text);
		this.minValue = aMinValue;
		this.maxValue = aMaxValue;
		labels = new ArrayList<GradeLabel>(aMaxValue - aMinValue);
		for (int i = aMinValue; i <= aMaxValue; ++i) {
			labels.add(new GradeLabel("Label " + i, this, i));
		}

	}

	/**
	 * Copy constructor.
	 * 
	 * @param question question to copy properties from.
	 */
	public GradableQuestion(Question question) {
		super(question.getName(), question.getText());
		this.setEvalCategory(question.getEvalCategory());
		this.setQuestionId(question.getQuestionId());
		this.setQuestionType(question.getQuestionType());
		this.setMustBeAnswered(question.getMustBeAnswered());
		this.minValue = GradableQuestion.DEF_MIN_VALUE;
		this.maxValue = GradableQuestion.DEF_MAX_VALUE;
		labels = new ArrayList<GradeLabel>(maxValue - minValue + 1);
		if (question instanceof GradableQuestion) {
			List<GradeLabel> qLabels = ((GradableQuestion) question).getLabels();
			for (int i = minValue; i <= maxValue; ++i) {
				labels.add(new GradeLabel(qLabels.get(i - minValue).getLabel(), this, i));
			}
		} else {
			for (int i = minValue; i <= maxValue; ++i) {
				labels.add(new GradeLabel("Label " + i, this, i));
			}
		}
	}

	/**
	 * @return the minValue
	 */
	public Integer getMinValue() {
		return this.minValue;
	}

	/**
	 * @param aMinValue the minValue to set
	 */
	public void setMinValue(Integer aMinValue) {
		this.minValue = aMinValue;
	}

	/**
	 * @return the maxValue
	 */
	public Integer getMaxValue() {
		return this.maxValue;
	}

	/**
	 * @param aMaxValue the maxValue to set
	 */
	public void setMaxValue(Integer aMaxValue) {
		this.maxValue = aMaxValue;
	}

	@Override
	protected String[] getAdditionalExclAttr() {
		return EXCL_ADDITIONAL;
	}

	/**
	 * @return the labels
	 */
	public List<GradeLabel> getLabels() {
		return labels;
	}

	/**
	 * @param aLabels the labels to set
	 */
	public void setLabels(List<GradeLabel> aLabels) {
		this.labels = aLabels;
	}

	public String getLabelForValue(Integer labelValue) {
		for (GradeLabel label : labels) {
			if (label.getId().getValue().equals(labelValue)) {
				return label.getLabel();
			}
		}
		return null;
	}
}
